﻿using System;
using System.Collections.Generic;
using System.Text;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.esriSystem;
using System.Diagnostics;
using System.Collections.Specialized;

namespace CanSWAT.ArcSWAT.Soil
{
    /// <summary>
    /// Soil database from Glenn
    /// </summary>
    class SoilDatabase
    {
        public SoilDatabase(string fileGDBPath)
        {
            _gdbPath = fileGDBPath;
        }

        private string _gdbPath = "";
        private IFeatureWorkspace _workspace = null;
        private Dictionary<string, List<SoilDeriv>> _soilDerivs = null;
        private Dictionary<string, List<SoilComponent>> _soilComponents = null;
        private List<SoilPolygon> _soilPolygons = null;

        /// <summary>
        /// Display structure of soil polygon, soil component and soil derivs
        /// </summary>
        public void DisplayStructure()
        {
            SoilFrm f = new SoilFrm();
            f.SoilDatabase = this;
            f.ShowDialog();
        }

        public static void Display(string fileGDBPath)
        {
            SoilDatabase db = new SoilDatabase(fileGDBPath);
            db.DisplayStructure();
        }

        public static void Display()
        {
            SoilFrm f = new SoilFrm();
            f.ShowDialog();
        }

        /// <summary>
        /// Open workspace
        /// </summary>
        private IFeatureWorkspace Workspace
        {
            get
            {
                if (_workspace == null)
                {
                    if (!System.IO.Directory.Exists(_gdbPath)) return null;
                    IWorkspaceFactory wf = new FileGDBWorkspaceFactory();
                    try
                    {
                        _workspace = wf.OpenFromFile(_gdbPath, 0) as IFeatureWorkspace;
                    }
                    catch(System.Exception e)
                    {
                        System.Diagnostics.Debug.WriteLine(e);
                        return null;
                    }
                }
                return _workspace;
            }
        }

        /// <summary>
        /// Open datatable using given name
        /// </summary>
        /// <param name="tableName"></param>
        /// <returns></returns>
        private ITable openTable(string tableName)
        {
            if (Workspace == null) return null;

            IWorkspace2 wp2 = Workspace as IWorkspace2;
            if (!wp2.get_NameExists(esriDatasetType.esriDTTable, tableName))
            {
                throw new System.Exception("Can't find " + tableName + " in " + _gdbPath);
            }
            return Workspace.OpenTable(tableName);
        }

        /// <summary>
        /// Open feature class under given feature dataset using given name
        /// </summary>
        /// <param name="datasetName">Name of feature dataset</param>
        /// <param name="featureClassName">Name of feature class</param>
        /// <returns></returns>
        private IFeatureClass openSoilPolygonFeatureClass(string datasetName, string featureClassName)
        {
            if (Workspace == null) return null;
            
            IWorkspace2 wp2 = Workspace as IWorkspace2;
            if (!wp2.get_NameExists(esriDatasetType.esriDTFeatureDataset, datasetName)) 
                return null;

            IFeatureDataset soilDataset = Workspace.OpenFeatureDataset(datasetName);
            IEnumDataset childDatasets = soilDataset.Subsets;
            IDataset d = childDatasets.Next();
            while (d != null)
            {
                if (d.BrowseName == featureClassName) return d as IFeatureClass;
                d = childDatasets.Next();
            }
            return null;
        }

        /// <summary>
        /// All polygons in soil map
        /// </summary>
        /// <remarks>Read from feature class LaSalle_Soils under feature dataset Soils. Has ability to read from other feature class</remarks>
        public List<SoilPolygon> SoilPolygons
        {
            get
            {
                if (_soilPolygons == null)
                {
                    _soilPolygons = new List<SoilPolygon>();

                    IFeatureClass polygonClass = openSoilPolygonFeatureClass("Soils", "LaSalle_Soils");
                    if (polygonClass == null) return _soilPolygons;

                    IFeatureCursor cursor = polygonClass.Search(null, false);
                    try
                    {
                        IFeature fea = cursor.NextFeature();
                        while (fea != null)
                        {
                            SoilPolygon polygon = new SoilPolygon(fea);
                            polygon.Components = SoilComponents[polygon.ID];
                            _soilPolygons.Add(polygon);

                            fea = cursor.NextFeature();
                        }
                    }
                    finally
                    {
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(cursor);
                    }

                }
                return _soilPolygons;
            }
        }

        /// <summary>
        /// All soil components
        /// </summary>
        private Dictionary<string, List<SoilComponent>> SoilComponents
        {
            get
            {
                if (_soilComponents == null)
                {
                    _soilComponents = new Dictionary<string, List<SoilComponent>>();

                    ITable derivTable = openTable("subbasin_cmp");
                    if (derivTable == null) return _soilComponents;

                    ICursor cursor = derivTable.Search(null, false);
                    try
                    {
                        IRow r = cursor.NextRow();
                        while (r != null)
                        {
                            SoilComponent cmp = new SoilComponent(r);
                            if (cmp.SOIL_ID.Length > 0 && cmp.ID.Length > 0)
                            {                                
                                cmp.Derivs = SoilDerivs[cmp.SOIL_ID];

                                if (!_soilComponents.ContainsKey(cmp.ID))
                                    _soilComponents.Add(cmp.ID, new List<SoilComponent>());
                                _soilComponents[cmp.ID].Add(cmp);
                            }

                            r = cursor.NextRow();
                        }
                    }
                    finally
                    {
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(cursor);
                    }
                }
                return _soilComponents;
            }
        }

        /// <summary>
        /// All soil derives
        /// </summary>
        private Dictionary<string, List<SoilDeriv>> SoilDerivs
        {
            get
            {
                if (_soilDerivs == null)
                {
                    _soilDerivs = new Dictionary<string, List<SoilDeriv>>();

                    ITable derivTable = openTable("subbasin_deriv");
                    if (derivTable == null) return _soilDerivs;

                    ICursor cursor = derivTable.Search(null, false);
                    try
                    {
                        IRow r = cursor.NextRow();
                        while (r != null)
                        {
                            SoilDeriv deriv = new SoilDeriv(r);
                            if (deriv.ID.Length > 0 && deriv.Depth >= 0) //don't consider layers above ground
                            {
                                if (!_soilDerivs.ContainsKey(deriv.ID))
                                    _soilDerivs.Add(deriv.ID, new List<SoilDeriv>());
                                _soilDerivs[deriv.ID].Add(deriv);
                            }

                            r = cursor.NextRow();
                        }

                        foreach (List<SoilDeriv> derivs in _soilDerivs.Values)
                        {
                            derivs.Sort(
                                delegate(SoilDeriv p1, SoilDeriv p2)
                                {
                                    return p1.Depth.CompareTo(p2.Depth);
                                }
                            );
                        }
                    }
                    finally
                    {
                        System.Runtime.InteropServices.Marshal.ReleaseComObject(cursor);
                    }
                }
                return _soilDerivs;
            }
        }

        private static string USER_SOIL_TABLE_HEADER_2009 =
            "MUID,SEQN,SNAM,S5ID,CMPPCT,NLAYERS,HYDGRP,SOL_ZMX,ANION_EXCL,SOL_CRK,TEXTURE,SOL_Z1,SOL_BD1,SOL_AWC1,SOL_K1,SOL_CBN1,CLAY1,SILT1,SAND1,ROCK1,SOL_ALB1,USLE_K1,SOL_EC1,SOL_Z2,SOL_BD2,SOL_AWC2,SOL_K2,SOL_CBN2,CLAY2,SILT2,SAND2,ROCK2,SOL_ALB2,USLE_K2,SOL_EC2,SOL_Z3,SOL_BD3,SOL_AWC3,SOL_K3,SOL_CBN3,CLAY3,SILT3,SAND3,ROCK3,SOL_ALB3,USLE_K3,SOL_EC3,SOL_Z4,SOL_BD4,SOL_AWC4,SOL_K4,SOL_CBN4,CLAY4,SILT4,SAND4,ROCK4,SOL_ALB4,USLE_K4,SOL_EC4,SOL_Z5,SOL_BD5,SOL_AWC5,SOL_K5,SOL_CBN5,CLAY5,SILT5,SAND5,ROCK5,SOL_ALB5,USLE_K5,SOL_EC5,SOL_Z6,SOL_BD6,SOL_AWC6,SOL_K6,SOL_CBN6,CLAY6,SILT6,SAND6,ROCK6,SOL_ALB6,USLE_K6,SOL_EC6,SOL_Z7,SOL_BD7,SOL_AWC7,SOL_K7,SOL_CBN7,CLAY7,SILT7,SAND7,ROCK7,SOL_ALB7,USLE_K7,SOL_EC7,SOL_Z8,SOL_BD8,SOL_AWC8,SOL_K8,SOL_CBN8,CLAY8,SILT8,SAND8,ROCK8,SOL_ALB8,USLE_K8,SOL_EC8,SOL_Z9,SOL_BD9,SOL_AWC9,SOL_K9,SOL_CBN9,CLAY9,SILT9,SAND9,ROCK9,SOL_ALB9,USLE_K9,SOL_EC9,SOL_Z10,SOL_BD10,SOL_AWC10,SOL_K10,SOL_CBN10,CLAY10,SILT10,SAND10,ROCK10,SOL_ALB10,USLE_K10,SOL_EC10";

        private static string USER_SOIL_TABLE_HEADER_2012 =
            USER_SOIL_TABLE_HEADER_2009 + ",SOL_CAL1,SOL_CAL2,SOL_CAL3,SOL_CAL4,SOL_CAL5,SOL_CAL6,SOL_CAL7,SOL_CAL8,SOL_CAL9,SOL_CAL10,SOL_PH1,SOL_PH2,SOL_PH3,SOL_PH4,SOL_PH5,SOL_PH6,SOL_PH7,SOL_PH8,SOL_PH9,SOL_PH10";

        public string ToUserSoilTableString(ArcSWATVersion version)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(version == ArcSWATVersion.A2009 ? USER_SOIL_TABLE_HEADER_2009 : USER_SOIL_TABLE_HEADER_2012);

            StringCollection uniqueComponents = new StringCollection(); //could be used to generate the soil lookup table

            try
            {
                foreach (SoilPolygon p in SoilPolygons)
                {
                    SoilComponent cmp = p.MergedComponent;
                    if (uniqueComponents.Contains(cmp.SOIL_ID)) continue;

                    uniqueComponents.Add(cmp.SOIL_ID);
                    sb.AppendLine(cmp.ToUserSoilTableString(version));
                }
            }
            catch (System.Exception e)
            {
                return e.Message + e.StackTrace;
            }
 
            return sb.ToString();
        }
    }  
}
